package algorythm;

import java.awt.Point;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import algorythm.Piece.Color;
import algorythm.Piece.PieceType;

public final class Algorythm 
{
	private Algorythm()
	{

	}

	public  static Object[] getBestMove(Map<Point, Piece> boardPositions,Piece.Color color)
	{
		Piece tempPiece=null;
		Object[] bestChoice = new Object[3];
		Object[] tempChoice = new Object[3];
		bestChoice[1] = 0;
		List <Point> possibleMoves;
		int bestValue = -1, tempValue=0;
		for(Map.Entry<Point, Piece> entry : boardPositions.entrySet())
		{
			tempPiece = entry.getValue();
			if(tempPiece.getColor() == color && tempPiece != null)
			{
				possibleMoves = tempPiece.possibleMoves(boardPositions,tempPiece,tempPiece.getPosition());
				if(possibleMoves.size()>0)
				{
					tempChoice = getBestPieceMove(possibleMoves,boardPositions); 
					tempValue = (Integer)tempChoice[1];

					if(tempValue > bestValue)
					{
						bestChoice[0] = (Point)tempChoice[0];
						bestChoice[1] = (Piece)tempPiece;
						bestChoice[2] = (int)tempChoice[1];
						bestValue = (int)tempChoice[1];
					}
				}
			}
		}
		return bestChoice;
	}

	private static Object[] getBestPieceMove(List<Point> possibleMoves, Map<Point, Piece> boardPositions)
	{
		Piece pieceToEat = null;
		Point bestCoordinate = null;
		Object[] bestChoice = new Object[2];
		int bestValue=-1;
		for(Point coordinate : possibleMoves)
		{
			pieceToEat = boardPositions.get(coordinate);

			if(pieceToEat != null && pieceToEat.getValue() > bestValue)
			{
				bestCoordinate=coordinate;
				bestValue = pieceToEat.getValue();			
			}
			if(pieceToEat == null && 0 > bestValue)
			{
				bestCoordinate = coordinate; 
				bestValue=0;
			}
		}
		bestChoice[0] = bestCoordinate;
		bestChoice[1] = bestValue;
		return bestChoice;
	}

	public boolean check(Map<Point, Piece> boardPositions,Piece.Color color)
	{
		if(color.equals("BLACK"))
		{
			color = Piece.Color.WHITE;
		}
		else
		{
			color = Piece.Color.BLACK;
		}
		Object[] bestMove = getBestMove(boardPositions, color);
		if((int)bestMove[2]>500)
		{
			return true;
		}
		return false;
	}

	public boolean checkMate(Map<Point, Piece> boardPositions,Piece.Color color)
	{
		if(check(boardPositions,color))
		{
			Piece tempPiece = null;
			Map<Point, Piece> tempBoardPositions = null;
			List<Point> possibleMoves;
			for(Map.Entry<Point, Piece> entry : boardPositions.entrySet())
			{
				if(entry.getValue().getColor().equals(color))
				{
					tempPiece = entry.getValue();
					possibleMoves = tempPiece.possibleMoves(boardPositions,tempPiece,tempPiece.getPosition());
					for(Point tempMove : possibleMoves)
					{
						tempBoardPositions = boardPositions;
						tempBoardPositions.put(tempMove, tempPiece);
						if(!check(tempBoardPositions, color))
						{
							return false;
						}
					}
				}
			}
			return true;
		}
		return false;
	}

	public List<Point> castling(Map<Point, Piece> boardPositions,Piece.Color color)
	{
		List<Point> castlingMoves = new ArrayList<Point>();
		if(color == Color.BLACK)
		{
			if(boardPositions.get(new Point(4,0)).getType() == PieceType.KING  &&
					boardPositions.get(new Point(5,0))==null &&
					boardPositions.get(new Point(6,0))==null &&
					boardPositions.get(new Point(7,0)).getType() == PieceType.TOWER)
			{
				castlingMoves.add(new Point(6,0));
			}
			if(boardPositions.get(new Point(4,0)).getType() == PieceType.KING &&
					boardPositions.get(new Point(3,0))==null &&
					boardPositions.get(new Point(2,0))==null &&
					boardPositions.get(new Point(1,0))==null &&
					boardPositions.get(new Point(0,0)).getType() == PieceType.TOWER)
			{
				castlingMoves.add(new Point(2,0));
			}

		}
		else if(color == Color.WHITE)
		{
			if(boardPositions.get(new Point(4,0)).getType() == PieceType.KING &&
					boardPositions.get(new Point(5,7))==null &&
					boardPositions.get(new Point(6,7))==null &&
					boardPositions.get(new Point(7,7)).getType() == PieceType.TOWER)
			{
				castlingMoves.add(new Point(6,0));
			}
			if(boardPositions.get(new Point(4,7)).getType()== PieceType.KING &&
					boardPositions.get(new Point(3,7))==null &&
					boardPositions.get(new Point(2,7))==null &&
					boardPositions.get(new Point(1,7))==null &&
					boardPositions.get(new Point(0,7)).getType() == PieceType.TOWER)
			{
				castlingMoves.add(new Point(2,0));
			}
		}
		else
		{
			System.err.println("Los if no estan funcionando");
		}
		return null;
	}

	@SuppressWarnings("unchecked")
	public static Object[] getBestMove2(HashMap<Point, Piece> boardPositions, Color colorToMove) {
		//System.err.println("1Buscando Mejor jugada de "+colorToMove);
		Double currentValue = new Double(0);
		Double maxAlpha = Double.NEGATIVE_INFINITY, obtainedValue;
		HashMap<Point, Piece> boardWithNextMove;
		Piece bestPice = null;
		Point bestLocation = null;
		for (Map.Entry<Point, Piece> entry : boardPositions.entrySet()) {
			if (entry.getValue() == null || entry.getValue().getColor() != colorToMove) {
				//System.out.println("...");
				continue;
			}
			//System.out.println("****************************Pieza: ****************************\n"+entry.getValue()+"\nPosicion en la que se encuentra en tablero actual: "+entry.getKey().x+","+entry.getKey().y+"\n********************************************************");
			//System.out.println("****************************Movimientos****************************");
			for (Point newPieceLocation : entry.getValue().possibleMoves(boardPositions, entry.getValue(), entry.getKey())) {
				if (boardPositions.get(newPieceLocation) != null) {
					currentValue = new Double(boardPositions.get(newPieceLocation).getValue());
				} else {
					currentValue = new Double(0);
				}
				boardWithNextMove = (HashMap<Point, Piece>) boardPositions.clone();
				boardWithNextMove.remove(entry.getKey());
				boardWithNextMove.put(newPieceLocation, entry.getValue());
				obtainedValue = minimaxAlphaBetaPruning(3, maxAlpha, Double.POSITIVE_INFINITY, boardWithNextMove, colorToMove);
				if ((currentValue - obtainedValue) > maxAlpha) {
					maxAlpha = (currentValue - obtainedValue);
					System.out.println("Nuevo maximo: "+maxAlpha);
					bestLocation = newPieceLocation;
					bestPice = entry.getValue();
				}
			}
			//System.out.println("****************************No Mas Movimientos****************************");
		}
		Object[] ret = {bestLocation, bestPice};
		return ret;
	}

	@SuppressWarnings("unchecked")
	private static Double minimaxAlphaBetaPruning(int level , Double alphaValue, Double betaValue, HashMap<Point, Piece> boardPositions, Color colorToMove) {
		Double currentValue = new Double(0);
		//System.err.println("2Buscando Mejor jugada de "+colorToMove);
		if (level == 0) { // ya que siempre sera impar, solo nos tenemos que concentrar en obtener el mejor
			//System.err.println("Returning getBestMove");
			return new Double((int) getBestMove(boardPositions, colorToMove)[2]);
		}
		HashMap<Point, Piece> boardWithNextMove;
		Double minimaxer, obtainedValue;
		if (level % 2 == 0) {//Actual level maximize
			minimaxer = Double.NEGATIVE_INFINITY;
		} else { //Actual level minimize 
			minimaxer = Double.POSITIVE_INFINITY;
		}
		for (Map.Entry<Point, Piece> entry : boardPositions.entrySet()) {
			if (entry.getValue() == null || entry.getValue().getColor() != colorToMove) {
				//System.out.println("...");
				continue;
			}
			//System.out.println("****************************Pieza: ****************************\n"+entry.getValue()+"\nPosicion en la que se encuentra en tablero actual: "+entry.getKey().x+","+entry.getKey().y+"\n********************************************************");
			//System.out.println("****************************Movimientos****************************");
			for (Point newPieceLocation : entry.getValue().possibleMoves(boardPositions, entry.getValue(), entry.getKey())) {
				if (boardPositions.get(newPieceLocation) != null) {
					currentValue = new Double(boardPositions.get(newPieceLocation).getValue());
				} else {
					currentValue = new Double(0);
				}
				//System.out.println("Moviendo a: "+newPieceLocation);
				boardWithNextMove = (HashMap<Point, Piece>) boardPositions.clone();
				boardWithNextMove.remove(entry.getKey());
				boardWithNextMove.put(newPieceLocation, entry.getValue());
				if (level % 2 == 0) {//Actual level maximize
					obtainedValue = Algorythm.minimaxAlphaBetaPruning((level-1), minimaxer, betaValue, boardWithNextMove, colorToMove.getEnemyColor());
					if ((currentValue - obtainedValue) > betaValue) {
						minimaxer = Double.NEGATIVE_INFINITY;
						break;
						//return Double.NEGATIVE_INFINITY;
					}
					minimaxer = Math.max((currentValue - obtainedValue), minimaxer);		
				} else { //Actual level minimize 
					obtainedValue = Algorythm.minimaxAlphaBetaPruning((level-1), alphaValue, minimaxer, boardWithNextMove, colorToMove.getEnemyColor());
					if (((currentValue - obtainedValue) * -1) < alphaValue) {
						minimaxer = Double.POSITIVE_INFINITY;
						break;
						//return Double.POSITIVE_INFINITY;
					} 
					minimaxer = Math.min((currentValue - obtainedValue), minimaxer);		
				}
			}
		}
		return minimaxer; 
	}

}	